闲话 22.10.4

闲话

今日休息
所以我看着一堆人在打PCK
“码力是很重要的”
所以我没去打
于是今天的杂题有两道!
写完这句话后开了第三题 发现这是nmd结论题
于是今天的杂题有三道

我是什么废物两个小时实现不出来这个结论
于是今天的杂题还是两道
行吧我题读错了 怪不得我模不出样例
所以今天的杂题还是三道

似乎上次自由时间我写了 4 道题来着
所以我在退步(确信)

晚饭回来更新:
路上在躲跑餐的学生们
然后往旁边一躲
脚下躺着的楼号牌:不好意思,这里满员了
然后踩了上去
然后一滑
膝盖:Full Combo~


70億の秘密と同じだけの

純粋が欲しくなるのは何故?

何が正義か

そんな馬鹿正直なふたりが

わからないよと泣いてる

杂题

蓝智力相当高,尤其擅长数学。据说连人类所无法想象程度的计算都能够在瞬间完成。——《东方求闻史纪》

CF1687D

定义 f(x) 表示严格大于 x 的最小的完全平方数,定义 g(x) 为小于等于 x 的最大的完全平方数。例如,f(1)=f(2)=g(4)=g(8)=4。蓝认为,一个正整数是“可爱”的,当且仅当 xg(x)<f(x)x,例如,1,5,11 是可爱的正整数,而 3,8,15 不是。

蓝给了你一个长度为 n 的正整数数列 ai,你需要帮她找到最小的非负整数 k,使得  iai+k 是可爱的。

n106,aiai+12×106

打表题。性质题。

观察 打表 经过严密的数学证明可得,可爱的数组成了一段段区间,第 x 段区间可以表为 [x2,x(x+1)]
a1 位于第 (ana1) 段区间内时定能得到答案,因此 k<(ana1)2

假设 a1 被放在了第 v 段内。可以发现 k 共有 v+1 种取值。
k 变化时,可能存在原来是可爱数的 ai+k 变为不可爱的数,反之亦然。
当一个元素的性质发生变化时,就无法通过 k 的继续变化使得其性质发生第二次变化。这是因为 v 段后的所有段长度定 >v,其长度大于 k 可能的变化范围。
可以发现,这其实限制了 k 的上下界。

我们初始时令 a1+k 位于第 v 段的段首。然后我们向后扫每一段
对于可爱段,我们找到这段内最大的数,这个数标定了 k 取值的上界,因为不能让这个数出可爱段。对于不可爱段,我们找到这段内最小的数,这个数标定了 k 取值的下界,因为需要让这个数增加到离开不可爱段。
于是我们每次扫一下就能得到答案。

这么写的复杂度似乎是 anlogan,具体是不是线性没想。

code
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define rep(i,a,b) for (register int (i) = (a); (i) <= (b); ++(i))
#define pre(i,a,b) for (register int (i) = (a); (i) >= (b); --(i))
const int N = 1e6 + 10;
int n, a[N], vis[N<<2], nxt[N<<2], pref[N<<2];

signed main() {
    ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
    cin >> n; rep(i,1,n) cin >> a[i];
    rep(i,2,n) vis[a[i] - a[1]] = 1;
    nxt[4000001] = 1e7, pref[0] = -1;
    rep(i,1,4000000) if (vis[i]) pref[i] = i; else pref[i] = pref[i-1];
    pre(i,4000000,1) if (vis[i]) nxt[i] = i;  else nxt[i] = nxt[i+1];

    rep(i,1,a[n]) {
        if (i * (i+1) < a[1]) continue;
        int l = 0, r = i; if (a[1] > i * i) l = a[1] - i * i;
        
        int nowl = 0, nowr = i;
        for (int j = i ; ; j++) {
            if (pref[nowr] >= nowl) r = min(r, nowr - pref[nowr]);
            nowl += 2 * j + 1;
            if (nxt[nowr+1] < nowl) l = max(l, nowl - nxt[nowr+1]);
            nowr += 2 * j + 2;
            if (l > r or nowl > a[n] - a[1]) break;
        }

        if (l <= r) {
            cout << i * i + l - a[1] << '\n';
            return 0; 
        }
    }
}



CF1700F

给定两个 2×n01 矩阵 AB,定义一次操作为交换 A 中任意两个相邻的位置中的值,输出使得 A=B 的最小操作次数,如果无法使 A=B 则输出 1

n2×105.

贪心题。

当矩阵是 1×n 的时候,答案是易求的。
sumai=j=1ia[i],sumbi=j=1ib[i],则最终答案就是

i=1n|sumaisumbi|

证明考虑 a 那边多 k 个 1 的情况,直到 b 那边出现 1 为止都得带着这 k 个 1 一直移动,移动一格的花费是 k=sumaisumbib 多 1 的情况同理,因此加绝对值。

现在矩阵是 2×n 的了,我们需要得到上下交换的最小花费。
分别记录两个前缀和 sum1sum2 分别表示两矩阵第一行的前缀和之差与第二行的前缀和之差。
在当前情况下上下交换当且仅当 sum1sum2 异号。当同号时上下交换是不必要的,这时交换肯定不会使答案更优。
异号时应当上下移动至 sum1sum2 之一为 0。这样做可以使得全局左右交换的次数最少,肯定不会使答案更劣。
上下移动后应当在答案中加入 sum1+sum2,原因同 1×n 理。

记得开ll。

code
#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for (register int (i) = (a); (i) <= (b); ++(i))
#define pre(i,a,b) for (register int (i) = (a); (i) >= (b); --(i))
#define sgn(u) ((u == 0) ? 0 : (u > 0) ? 1 : -1 )
const int N = 2e5 + 10;
int n, a[2][N], b[2][N];
long long cnt, ans;

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n;
    rep(i,0,1) rep(j,1,n) cin >> a[i][j], cnt += a[i][j];
    rep(i,0,1) rep(j,1,n) cin >> b[i][j], cnt -= b[i][j];
    if (cnt) { cout << -1 << '\n'; return 0; }
    long long sum0 = 0, sum1 = 0, delt;
    rep(i,1,n) {
        sum0 += a[0][i] - b[0][i], sum1 += a[1][i] - b[1][i];
        if (1ll * sum0 * sum1 < 0) {
            delt = min(abs(sum0), abs(sum1));
            ans += delt;
            sum0 += -sgn(sum0) * delt;
            sum1 += -sgn(sum1) * delt;
        }
        ans += abs(sum0) + abs(sum1);
    } cout << ans << endl;
} 



CF1685C

给定一个长度为 2n 的括号序列 s,保证其由 n 个左括号 (n 个右括号 ) 组成。定义一次操作是翻转 s 的一个连续子串。请求出最少几次操作可将 s 转换为合法括号序列。输出最少的操作次数,并构造一种方案。可以证明,这总是能在 n 次操作中完成。

n2×105

结论题。

注意这里的操作定义是这样的:设我们用 str 数组从下标 1 位置开始存储这个序列,则对 [l,r] 序列的操作等价于 reverse(str+l,str+r+1)。无论如何操作,左括号和右括号的数量不会发生变化,因此 a2n 恒等于 0

括号序列的转化是 (1)1,合法括号序列是任意位置非负。
设括号序列 s 的前缀和数组为 ai。若 i,ai0 则不需要操作,此后讨论默认 s 不是合法括号序列。

我们断言,使用不超过两次操作就可以得到合法括号序列。

证明

我们只需要找到前缀和数组的最大值 ak,随后翻转 [1,k][k+1,2n] 即可。

设翻转后的前缀和数组为 bi
ik 时,bi 对应的是原序列 [i,k] 段的和,即 akai。由于 ak 最大,因此此情况下 bi0
i>k 时,bi 对应的是原序列 [1,k] 段和 [i,2n] 段的和,即 ak+a2nai,其中 ii 翻转后对应位置。由于 a2n=0ak 最大,因此此情况下 bi0

因此我们只需要判断是否能仅操作一次。
[L,R] 分别为最前和最后一个 a 小于 0 的位置。我们断言,最终翻转的区间 [l,r] 需要满足 lL R<r,并且最优选择一定是 al1 最大的 lar 最大的 r。不保证最优选择在操作后一定得到合法括号序列。

证明

可以知道最终 [1,l)(r,2n] 的前缀和没有变化。这表明最终翻转的区间 [l,r] 需要满足 lL R<r
考察 [l,r] 段新的前缀和 bi,立即列出方程:bi=al1+arai,其中 ii 翻转后对应位置。
则当 al1+arai0al1+arai 时有操作一次即可得到合法括号序列。
我们只需要贪心地选择最大值即可得到最优选择。

选择后只需要检验一次,若可行则输出 1,若不可行则输出 2。
构造方案平凡,上方已给出。

总时间复杂度 O(n)

code
#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for (register int (i) = (a); (i) <= (b); ++(i))
#define pre(i,a,b) for (register int (i) = (a); (i) >= (b); --(i))
const int N = 4e5 + 10;
int T, n, mpos, pref[N];
char ch[N];

bool check(int l, int r) {
    reverse(ch+l, ch+r+1);

    rep(i,l,r) {
        pref[i] = pref[i-1] + (ch[i] == '(' ? 1 : -1);
        if (pref[i] < 0) return false;
    } 
    rep(i,r+1,n) {
        pref[i] = pref[i-1] + (ch[i] == '(' ? 1 : -1);
        if (pref[i] < 0) return false;
    } 

    return true;
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> T;
    while (T--) {
        cin >> n >> ch + 1; n <<= 1; 
        bool completed = true; mpos = 1; pref[n+1] = -1e9;

        rep(i,1,n) {
            pref[i] = pref[i-1] + (ch[i] == '(' ? 1 : -1);
            if (pref[mpos] < pref[i]) mpos = i;
            if (pref[i] < 0) completed = false;
        } 
        if (completed) { cout << 0 << '\n'; continue; };

        int lmst = 1, rmst = n;
        while (lmst <= n and pref[lmst] >= 0) lmst ++;
        while (rmst > 0 and pref[rmst] >= 0) rmst --;

        int lck = n+2, rck = n+1;
        rep(i,1,lmst) if (pref[lck-1] < pref[i-1]) lck = i;
        rep(i,rmst+1,n) if (pref[rck] < pref[i]) rck = i;

        if (check(lck, rck)) {
            cout << 1 << '\n';
            cout << lck << ' ' << rck << '\n';
        }
        else {
            cout << 2 << '\n';
            cout << 1 << ' ' << mpos << '\n';
            cout << mpos+1 << ' ' << n << '\n';
        } 
    }
}
posted @   joke3579  阅读(95)  评论(7编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示